﻿using System;
using System.Diagnostics;

namespace g4
{
    /// <summary>
    /// 网格边界环挤出操作类
    /// 功能：对网格上的边界环（EdgeLoop）进行挤出，创建新的顶点环，并通过三角形环将原始环与新环连接
    /// 前提：输入的Loop必须是网格上的边界环（非内部环）
    /// </summary>
    public class MeshExtrudeLoop
    {
        /// <summary>
        /// 要操作的目标网格
        /// </summary>
        public DMesh3 Mesh;

        /// <summary>
        /// 要挤出的边界环（由一系列顶点按顺序组成的闭合环）
        /// </summary>
        public EdgeLoop Loop;

        // 挤出参数

        /// <summary>
        /// 用于计算新顶点位置的函数
        /// 输入：原始顶点位置、原始顶点法向量、顶点在环中的索引
        /// 输出：新顶点的位置
        /// </summary>
        public Func<Vector3d, Vector3f, int, Vector3d> PositionF;

        // 操作结果

        /// <summary>
        /// 挤出过程中创建的新三角形ID数组
        /// </summary>
        public int[] NewTriangles;

        /// <summary>
        /// 挤出后生成的新顶点环
        /// </summary>
        public EdgeLoop NewLoop;


        /// <summary>
        /// 构造函数：初始化挤出操作
        /// </summary>
        /// <param name="mesh">目标网格</param>
        /// <param name="loop">要挤出的边界环</param>
        public MeshExtrudeLoop(DMesh3 mesh, EdgeLoop loop)
        {
            Mesh = mesh;
            Loop = loop;

            // 默认的新顶点位置计算函数：沿Y轴正方向挤出（可通过外部重写自定义方向）
            PositionF = (pos, normal, idx) => {
                return pos + Vector3d.AxisY; // 原始位置加上Y轴单位向量（向上挤出）
            };
        }


        /// <summary>
        /// 验证操作的合法性：检查输入的环是否为有效的边界环
        /// </summary>
        /// <returns>验证状态（Valid表示合法，其他值表示错误类型）</returns>
        public virtual ValidationStatus Validate()
        {
            // 调用网格验证工具，检查当前环是否为有效的边界环
            ValidationStatus loopStatus = MeshValidation.IsBoundaryLoop(Mesh, Loop);
            return loopStatus;
        }


        /// <summary>
        /// 执行挤出操作
        /// </summary>
        /// <param name="group_id">新创建三角形的组ID（用于分类，默认-1表示无组）</param>
        /// <returns>操作是否成功</returns>
        public virtual bool Extrude(int group_id = -1)
        {
            // 1. 复制原始环的顶点，创建新环的顶点基础
            int vertexCount = Loop.Vertices.Length; // 环中的顶点数量
            NewLoop = new EdgeLoop(Mesh); // 初始化新环
            NewLoop.Vertices = new int[vertexCount]; // 新环的顶点数组（与原始环长度相同）

            // 为每个原始顶点创建副本，作为新环的顶点
            for (int i = 0; i < vertexCount; ++i)
            {
                int originalVid = Loop.Vertices[i]; // 原始环的顶点ID
                // 复制顶点（包括位置、法向量等属性），返回新顶点ID
                NewLoop.Vertices[i] = Mesh.AppendVertex(Mesh, originalVid);
            }

            // 2. 根据PositionF函数调整新环顶点的位置（核心挤出逻辑）
            for (int i = 0; i < vertexCount; ++i)
            {
                int originalVid = Loop.Vertices[i];
                Vector3d originalPos = Mesh.GetVertex(originalVid); // 原始顶点位置
                Vector3f originalNormal = Mesh.GetVertexNormal(originalVid); // 原始顶点法向量
                // 调用位置计算函数，得到新顶点位置
                Vector3d newPos = PositionF(originalPos, originalNormal, i);
                // 更新新顶点的位置
                Mesh.SetVertex(NewLoop.Vertices[i], newPos);
            }

            // 3. 缝合原始环与新环，创建连接的三角形环
            MeshEditor editor = new MeshEditor(Mesh); // 创建网格编辑器
            // 调用StitchLoop方法：在两个环之间创建三角形（每个相邻顶点对形成一个四边形，拆分为两个三角形）
            // 返回新创建的三角形ID数组
            NewTriangles = editor.StitchLoop(Loop.Vertices, NewLoop.Vertices, group_id);

            return true; // 挤出操作成功
        }
    }
}